import math
from collections import OrderedDict
from operator import itemgetter

import numpy as np
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets, filters, status
from rest_framework.pagination import PageNumberPagination
from rest_framework.utils.urls import replace_query_param

from goods.filters import GoodsFilter
from goods.models import GoodsCategory, Goods
from goods.serializers import CategorySerializer, GoodListSerializer, GoodSerializer
from operation.models import UserBehavior
from utils.mixins import CustomRetrieveModelMixin, CustomListModelMixin
from utils.response import APIResponse


class CategoryListViewSet(CustomListModelMixin, CustomRetrieveModelMixin, viewsets.GenericViewSet):
    """
    list:
        商品分类列表数据
    """

    def get_serializer_context(self):
        return {
            'format': self.format_kwarg,
            'view': self
        }

    queryset = GoodsCategory.objects.filter(category_level=1)
    serializer_class = CategorySerializer


class GoodsPagination(PageNumberPagination):
    def get_paginated_response(self, data):
        return APIResponse(results=OrderedDict([
            ('count', self.page.paginator.count),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
            ('results', data)
        ]), data_status=status.HTTP_200_OK)

    def get_next_link(self):
        if not self.page.has_next():
            return None
        url = self.request.get_full_path()
        page_number = self.page.next_page_number()
        return replace_query_param(url, self.page_query_param, page_number)

    page_size = 9
    page_size_query_param = 'page_size'
    page_query_param = 'page'


class GoodsListViewSet(CustomListModelMixin, viewsets.GenericViewSet):
    """
    list:
        商品列表数据
    """

    def get_serializer_context(self):
        return {
            'format': self.format_kwarg,
            'view': self
        }

    def get_queryset(self):
        return Goods.objects.all().order_by('id')

    serializer_class = GoodListSerializer
    pagination_class = GoodsPagination
    filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
    filter_fields = ('good_category__category_code',)
    filter_class = GoodsFilter
    search_fields = ('good_name', 'tags__tag')
    ordering_fields = ('shop_price', 'sold_num', 'id')


class GoodViewSet(CustomRetrieveModelMixin, viewsets.GenericViewSet):
    """s
        商品详情数据
    """

    def get_serializer_context(self):
        return {
            'format': self.format_kwarg,
            'view': self
        }

    def get_queryset(self):
        user = self.request.user
        good = Goods.objects.get(id=self.request.parser_context['kwargs']['pk'])
        queryset = UserBehavior.objects.all().filter(user=user, good=good)
        if queryset.count() == 0:
            user_behavior_instance = UserBehavior.objects.create(user=user, good=good)
        else:
            user_behavior_instance = queryset[0]
        user_behavior_instance.view_num += 1
        user_behavior_instance.save()
        return Goods.objects.all().order_by('id')

    serializer_class = GoodSerializer


class RecommendListViewSet(CustomListModelMixin, viewsets.GenericViewSet):
    """
    list:
        商品推荐列表数据
    """

    def get_serializer_context(self):
        return {
            'format': self.format_kwarg,
            'view': self
        }

    def get_queryset(self):
        user_behavior_queryset = UserBehavior.objects.values()
        dicc = dict()
        for item in user_behavior_queryset:
            good_id = item['good_id']
            user_id = item['user_id']
            if user_id not in dicc.keys():
                dicc[user_id] = set()
            dicc[user_id].add(good_id)
        similarity_dict = self.get_user_similarity(dicc)
        recommend = self.recommend(dicc, similarity_dict, 3)
        recommend_sorted = sorted(recommend.items(), key=lambda item: item[1], reverse=True)[0:2]
        recommend_sorted_keys = []
        for item in recommend_sorted:
            recommend_sorted_keys.append(item[0])
        print(recommend_sorted_keys)
        recommendSet = Goods.objects.filter(id__in=recommend_sorted_keys)
        if len(recommendSet) <= 1:
            rand = np.random.randint(101, 600, size=2)
            recommendSet = Goods.objects.filter(id__in=rand).order_by('update_time')[0:2]
        return recommendSet

    def get_user_similarity(self, dicc):
        # 建立倒排表
        item_user = dict()
        for u, items in dicc.items():
            for i in items:
                if i not in item_user.keys():
                    item_user[i] = set()
                item_user[i].add(u)
        C = dict()  # 用户与用户共同喜欢物品的个数
        N = dict()  # 用户具有正反馈行为的个数
        for item, users in item_user.items():
            for u in users:
                if u not in N.keys():
                    N[u] = 0
                N[u] += 1
                for v in users:
                    if u == v:
                        continue
                    if (u, v) not in C.keys():
                        C[u, v] = 0
                    C[u, v] += 1 / math.log(1 + len(users))
        W = dict()
        for co_user, cuv in C.items():
            W[co_user] = cuv / math.sqrt(N[co_user[0]] * N[co_user[1]])
        return W

    def recommend(self, dicc, W2, K):
        user = self.request.user.id
        rank = dict()
        related_user = []
        if user not in dicc.keys():
            return {}
        interacted_items = dicc[user]
        for co_user, item in W2.items():
            if co_user[0] == user:
                related_user.append((co_user[1], item))
        for v, wuv in sorted(related_user, key=itemgetter(1), reverse=True)[0:K]:
            for i in dicc[v]:
                if i in interacted_items:
                    continue
                if i not in rank.keys():
                    rank[i] = 0
                user_behavior = UserBehavior.objects.get(user=v, good=Goods.objects.get(id=i))
                rvi = user_behavior.view_num * 0.2 + user_behavior.purchase_num * 0.8
                rank[i] += wuv * rvi
        return rank

    serializer_class = GoodListSerializer
